-- MagicStoneM
-- Created by panyl
-- 魔法石

module("MagicStoneM", package.seeall);

-- 石柱配置
local stones = {};

-- 内部函数声明
local whenPlayerMove;
local whenOpenGrid;
local whenGridDisable;

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    -- 转换下stone信息表
    local stone_CSV = get_stone_CSV();
    for i = 1, #stone_CSV do
        stones[stone_CSV[i].id] = stone_CSV[i];
    end

    stone_CSV = {};
end

function init()
    loadCsv();

    -- 关注玩家离开地牢的事件
    EventMgr.removeAll("MagicStoneM");
    EventMgr.register("MagicStoneM", event.LEAVE_DUNGEON, function()
        ME.user.dbase:delete("stone");
        ME.user.dbase:delete("stone_prop");
        ME.user.dbase:deleteTemp("magic_stone");
    end);

    -- 关注玩家进入下一层的事件
    EventMgr.register("MagicStoneM", event.NEXT_FLOOR, function()
        -- 把上次的石柱数据删除
        ME.user.dbase:deleteTemp("magic_stone");

        -- 把上次石柱的负面状态删除
        CombatStatusM.clearStatusByRule(ME.user, "stone_prop")
    end);

    -- 每回合事件
    CombatM.registerEndRoundCB("magic_stone", whenPlayerMove);

    -- 开始迷宫
    EventMgr.register("MagicStoneM", event.CHANGE_GRID_STATE, whenGridDisable);

    -- 每回合事件
    EventMgr.register("MagicStoneM", event.OPEN_GRID, whenOpenGrid);
    EventMgr.register("MagicStoneM", event.PURE_OPEN_GRID, whenOpenGrid);
end

-- 检索
function query(id, path)
    local m = stones[id];

    if not m or not path then
        return m;
    end

    return m[path];
end

-- 获取石柱图标
function getStoneIcon(id)
    local icon;

    if isClosed() then
        -- 已关闭图标
        icon = query(id, "closed_icon")
    else
        -- 未关闭图标
        icon = query(id, "unclose_icon")
    end

    return icon;
end

-- 计算消耗
function calcCost(id)
    if isClosed() then
        -- 已关闭，返回学习消耗
        return query(id, "study_cost");
    else
        -- 未关闭，返回关闭消耗
        return query(id, "closed_cost");
    end
end

-- 抽取一个石柱
function fetchStone(pos, fixedId)
    -- 按固定顺序出现
    local id;
    if type(fixedId) == "number" then
        id = fixedId;
    else
        id = FormulaM.invoke("CALC_MAGIC_STONE_ORDER", ME.user)
    end

    ME.user.dbase:setTemp("magic_stone", {
        ["id"] = id,
        ["pos"] = pos,
        ["state"] = STONE_STATE_COVER,
    });

    return id;
end

-- 判断石柱是否关闭了
function isClosed()
    local magicStone = ME.user.dbase:queryTemp("magic_stone");

    -- 当前没有石柱
    if not magicStone or not magicStone["id"] then
        return true;
    end

    -- 如果已经关闭或者失效了
    if  magicStone["state"] == STONE_STATE_CLOSED or
        magicStone["state"] == STONE_STATE_DISABLE then
        return true;
    end

    return false;
end

-- 获取当前石柱的状态
function getStoneInfo(path)
    local magicStone = ME.user.dbase:queryTemp("magic_stone");

    if not magicStone or not path then
        return magicStone;
    end

    return magicStone[path];
end

-- 消耗
function doCost(cost)
    for key, amount in pairs(cost) do
        if type(key) == "number" then
            ItemM.costAmount(ME.user, key, amount);
        elseif type(key) == "string" then
            ME.user:costAttrib(key, amount);
        end
    end
end

-- 检查是否能解除
function checkClosed()
    -- 如果已经解除过了
    if getStoneInfo("state") ~= STONE_STATE_OPENED then
        trace("MagicStoneM", "当期石柱的状态不能关闭。")
        return false;
    end

    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    local id = magicStone["id"];
    local cost = stones[id]["closed_cost"] or {};

    local ok = true;

    for key, amount in pairs(cost) do
        if type(key) == "string" then
            -- 属性
            ok = ME.user.dbase:query(key, 0) >= amount;
        elseif type(key) == "number" then
            -- 物品
            ok = ItemM.getAmount(ME.user, key) >= amount;
        else
            -- 暂无其他消耗
            ok = false;
        end

        if not ok then
            trace("MagicStoneM", "%s不足，不能关闭石柱。", tostring(key));
            return ok;
        end
    end

    return ok;
end

-- 解除石柱
function closedStone()
    -- 判断下
    if not checkClosed() then
        return false;
    end

    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    local id = magicStone["id"];
    local pos = magicStone["pos"];

    -- 消耗
    local cost = stones[id]["closed_cost"] or {};
    doCost(cost);

    -- 标记关闭
    magicStone["state"] = STONE_STATE_CLOSED;

    -- 清除石柱负面状态
    CombatStatusM.clearStatusByRule(ME.user, "stone_prop")

    -- 标记解除过结界
    DungeonM.setDungeonMixedField("has_closed_stone", 1);

    DungeonLogM.addLog("解除石柱。");

    -- 抛出事件
    EventMgr.fire(event.CLOSED_STONE, {["id"] = id, ["pos"] = pos, });
    return true;
end

-- 判断能否学习
function checkStudy()
    -- 如果没有石柱信息
    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    if not magicStone or not magicStone["id"] then
        trace("MagicStoneM", "没有石柱信息，不能学习。");
        return false;
    end

    -- 只有关闭状态才能学习
    if getStoneInfo("state") ~= STONE_STATE_CLOSED then
        trace("MagicStoneM", "石柱当前不可学习。");
        return false;
    end

    -- 判断等级
    local id = magicStone["id"];
    local stoneData = ME.user.dbase:query("stone", {});
    local level = stoneData[id] or 0;
    if level >= stones[id]["max_level"] then
        trace("MagicStoneM", "石柱%d的技能已经学习到最高等级。", id);
        return "level_upper";
    end

    -- 判断消耗
    local cost = stones[id]["study_cost"];
    local ok = true;

    for key, amount in pairs(cost) do
        if type(key) == "string" then
            -- 属性
            ok = ME.user.dbase:query(key, 0) >= amount;
        elseif type(key) == "number" then
            -- 物品
            ok = ItemM.getAmount(ME.user, key) >= amount;
        else
            -- 暂无其他消耗
            ok = false;
        end

        if not ok then
            trace("MagicStoneM", "%s不足，不能学习石柱。", tostring(key));
            return "item_lack";
        end
    end

    return ok;
end

-- 学习
function studyStone()
    -- 判断下
    if true ~= checkStudy() then
        return false;
    end

    -- 1. 先做消耗
    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    local id = magicStone["id"];
    local pos = magicStone["pos"];
    local cost = stones[id]["study_cost"];

    doCost(cost);

    -- 2. 标记石柱失效
    local grid = DungeonM.getGridByPos(pos);
    grid:changeState(GRID_STATE_DISABLE);
    magicStone["state"] = STONE_STATE_DISABLE;

    -- 3. 学习
    local stoneData = ME.user.dbase:query("stone", {});
    local level = stoneData[id] or 0;
    stoneData[id] = level + 1;
    ME.user.dbase:set("stone", stoneData);

    -- 是否是新技能
    local isNew = iif(level == 0, true, false);

    -- 刷新下技能属性
    PropM.refreshEffect(ME.user, "skill");

    -- 抛出事件
    EventMgr.fire(event.STUDY_STONE, {["id"] = id, ["pos"] = pos, ["isNew"] = isNew,});

    -- 操作
    DungeonM.addAction({ ["cmd"] = "study_stone", ["pos"] = pos, });
    return true, true;
end

-- 获取等级
function getStoneLevel(stoneId)
    stoneId = stoneId or getStoneInfo("id");
    if not stoneId then
        return 0;
    end

    local stoneData = ME.user.dbase:query("stone", {});

    return stoneData[stoneId] or 0;
end

-- 根据技能获取石柱id
function getStoneIdBySkill(skillId)
    local stoneId;
    for id, _ in pairs(stones) do
        if getEffect(id)["skill_id"] == skillId then
            stoneId = id;
            break;
        end
    end

    return stoneId;
end

-- 计算技能消耗
function calcSkillCost(skillId)
    local stoneId = getStoneIdBySkill(skillId);

    -- 没找到石柱id
    if not stoneId then
        return;
    end

    -- 交给公式计算
    local mp = FormulaM.invoke("CALC_STONE_SKILL_COST", stoneId, skillId);

    return mp;
end

-- 是否是石柱技能
function isStoneSkill(skillId)
    local stoneId = getStoneIdBySkill(skillId);

    -- 没找到石柱id
    if not stoneId then
        return false;
    end

    return true;
end

-- 判断能否施放技能
function checkApply(skillId)
    local stoneId = getStoneIdBySkill(skillId);

    if not stoneId then
        trace("MagicStoneM", "没有此石柱技能。");
        return false;
    end

    local stoneData = ME.user.dbase:query("stone", {});
    if not stoneData[stoneId] then
        trace("MagicStoneM", "玩家尚未学习石柱%d的技能。", stoneId);
        return false;
    end

    if isForbidMagic() then
        -- 处于禁魔状态
        return "forbid_magic";
    end

    if isFullAttrib(skillId) then
        -- 增加属性的技能，如果学满了
        return "full_attrib";
    end

    -- 消耗
    local mp = calcSkillCost(skillId);
    if ME.user:getMp() < mp then
        trace("MagicStoneM", "玩家的魔法值不足，需求%d，当前%d。",
            mp, ME.user:getMp());
        return "mp_lack";
    end

    return true;
end

-- 使用技能
function apply(skillId, pos)
    -- 检查
    if true ~= checkApply(skillId) then
        return false;
    end

    -- 扣除魔法值
    local mp = calcSkillCost(skillId);

    ME.user:costAttrib("mp", mp);

    -- 施放技能
    -- 传入攻击者信息
    local assailantInfo = {
        ["skill_id"] = skillId,
        ["source"]   = ME.user,
    };
    SkillM.cast(ME.user, skillId, pos, {assailantInfo = assailantInfo, });

    local stoneId = getStoneIdBySkill(skillId);
    DungeonM.addAction({ ["cmd"] = "stone_skill", ["pos"] = pos, ["data"] = stoneId, });

    -- 抛出使用石柱魔法事件
    EventMgr.fire(event.USE_STONE_MAGIC, { ["stoneId"] = stoneId, });

    -- 一个回合事件
    EventMgr.fire(event.COMBAT_ROUND, pos);

    -- action已经添加
    return true, true;
end

-- 获取效果
function getEffect(id)
    local stoneData = ME.user.dbase:query("stone", {});
    if not stoneData[id] then
        return {};
    end

    local effect = query(id, "effect");
    local level = stoneData[id];

    -- 技能
    local skillId = SkillM.findSkill(effect["skill_id"], level);

    return {["skill_id"] = skillId, };
end

-- 获取石柱等级
function getStoneLevel(id)
    local stoneData = ME.user.dbase:query("stone", {});
    if not stoneData[id] then
        return 1;
    end

    return stoneData[id];
end

-- 获取负面效果
function getNegative(path)
    local id = getStoneInfo("id");
    if not id then
        return;
    end

    -- 如果还未翻开
    if getStoneInfo("state") ~= STONE_STATE_OPENED then
        return;
    end

    local negative = query(id, "negative");

    return negative[path];
end

-- 获取玩家的所有石柱技能
function getStoneSkills(skillType)
    local stoneData = ME.user.dbase:query("stone");
    if not stoneData then
        return {};
    end

    local ret = {};
    for id, _ in pairs(stoneData) do
        local data = getEffect(id);

        if  data["skill_id"] and
            SkillM.query(data["skill_id"], "type") == skillType then
            table.insert(ret, data["skill_id"]);
        end
    end

    return ret;
end

-- 判断是否处于禁魔状态
function isForbidMagic()
    local prop = PropM.combine(ME.user, "forbidden_magic", "stone");
    if PropM.apply(prop, 1) > 0 then
        return true;
    end
    return false;
end

-- 判断技能附加属性是否已满
function isFullAttrib(skillId)
    if not isStoneSkill(skillId) then
        -- 只处理石柱技能
        return false;
    end
    local ruleDesc = SkillM.query(skillId, "apply1")
    local _, __, rule, para = string.find(ruleDesc, "([^%(]+)%((.*)%)$");
    local arr = string.explode(para, ",");
    local attribName = arr[1];
    local max = tonumber(arr[3]);
    if attribName then
        local stoneProp = ME.user.dbase:query("stone_prop", {});
        local attrib = stoneProp[attribName] or 0;
        if attrib >= max then
            return true
        end
    end
    return false;
end

-- 是否干扰类格子
function isMagicGrid(gridType)
    if gridType == GRID_TYPE_STONE or gridType == GRID_TYPE_KRYPTONITE or gridType == GRID_TYPE_INTERFERE_DEVICE then
        return true;
    end

    return false;
end

-- 格子失效之后
whenGridDisable = function(para)
    if para.state ~= GRID_STATE_DISABLE then
        return;
    end

    -- 不是干扰类格子
    if not isMagicGrid(para.type) then
        return;
    end

    -- 服务器、客户端独自开启
    -- 如果当前没有魔法石柱的数据
    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    if not magicStone then
        return false;
    end

    -- 如果已经开启过了
    if getStoneInfo("state") == STONE_STATE_CLOSED then
        return false;
    end

    -- 如果不是石柱
    if para.pos ~= magicStone["pos"] then
        return false;
    end

    closedStone();
end

-- 每回合的回调
whenPlayerMove = function()
    -- 服务器、客户端独自开启
    -- 如果当前没有魔法石柱的数据
    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    if not magicStone then
        return false;
    end

    -- 如果已经开启过了
    if getStoneInfo("state") ~= STONE_STATE_COVER then
        return false;
    end

    local id = magicStone["id"];
    local pos = magicStone["pos"];
    local round = CombatM.getRound();

    printTable(magicStone, "当前魔石的信息");
    -- 回合数
    if round <= query(id, "appear") then
        return false;
    end

    -- 如果已经清怪，则无需再开格子。因为执行清怪奖励时，会将所有格子开启
    if DungeonM.isAllMonsterDead() then
        return false;
    end

    local grid = DungeonM.getGridByPos(pos);

    -- 格子已经翻开过了
    if grid:isOpened() then
        return false;
    end

    -- 直接翻开格子（不影响周围格子局势）
    grid:pureOpen();

    magicStone["state"] = STONE_STATE_OPENED;
    return true;
end

whenOpenGrid = function(para)
    if not isMagicGrid(para.type) then
        return;
    end
    local pos = para["pos"];
    if getStoneInfo("pos") ~= pos then
        return;
    end

    -- 打开的是石柱
    local magicStone = ME.user.dbase:queryTemp("magic_stone");
    magicStone["state"] = STONE_STATE_OPENED;

    -- 石柱负面状态
    local stoneStatus = getNegative("status");
    printTable(stoneStatus, "whenOpenGrid负面状态");

    if stoneStatus then
        for _, status in pairs(stoneStatus) do
            local alias = status["status"];
            local condition = table.deepcopy(status);
            condition["status"] = nil;

            -- TODO: 诅咒、氪石结界受诅咒免疫影响，先做一下特殊处理
            if alias == "dark_stone" or alias == "super_stone" then
                local arr = PropM.combine(ME.user, "immunity", "curse");
                if PropM.apply(arr, 1) == 0 then
                    CombatStatusM.applyStatus(ME.user, alias, condition);
                end
            else
                CombatStatusM.applyStatus(ME.user, alias, condition);
            end
        end
    end
end
